package com.hugehard.ImageFilter;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

import android.graphics.Bitmap;
import android.graphics.Color;

public class Kuwahara {
	public Bitmap kuwaharaFilter(Bitmap image, int n) {
		Bitmap postImage = Bitmap.createBitmap(image.getWidth(),
				image.getHeight(), Bitmap.Config.ARGB_8888);

		for (int x = 0; x < image.getWidth(); x++) {
			for (int y = 0; y < image.getHeight(); y++) {
				int rgb = processPixel(image, x, y, n);
				postImage.setPixel(x, y, rgb);
			}
		}
		return postImage;
	}

	private int processPixel(Bitmap image, int x, int y, int n) {
		if (n % 2 == 0) {
			return -1;
		}

		int areaLength = (n + 1) / 2;
		AreaInfo areaA = calculateNaiveVariance(image, x - (areaLength - 1), y
				- (areaLength - 1), areaLength);
		AreaInfo areaB = calculateNaiveVariance(image, x, y - (areaLength - 1),
				areaLength);
		AreaInfo areaC = calculateNaiveVariance(image, x - (areaLength - 1), y,
				areaLength);
		AreaInfo areaD = calculateNaiveVariance(image, x, y, areaLength);

		ArrayList<AreaInfo> list = new ArrayList<AreaInfo>();
		list.add(areaA);
		list.add(areaB);
		list.add(areaC);
		list.add(areaD);
		Collections.sort(list, new AreaInfoComparator());

		AreaInfo leastVariance = (AreaInfo) list.get(0);

		return Color.rgb((int) leastVariance.mMeanRed,
				(int) leastVariance.mMeanGreen, (int) leastVariance.mMeanBlue);
	}

	private AreaInfo calculateNaiveVariance(Bitmap image, int startX,
			int startY, int n) {
		int endX = startX + n;
		int endY = startY + n;
		int totalRed = 0;
		int totalBlue = 0;
		int totalGreen = 0;
		int totalRedSqr = 0;
		int totalBlueSqr = 0;
		int totalGreenSqr = 0;
		int count = 0;
		double meanRed = 0;
		double meanBlue = 0;
		double meanGreen = 0;
		double varianceRed = 0;
		double varianceBlue = 0;
		double varianceGreen = 0;
		double varianceTotal = 0;

		startX = startX < 0 ? 0 : startX;
		startY = startY < 0 ? 0 : startY;
		endX = endX > image.getWidth() ? image.getWidth() : endX;
		endY = endY > image.getHeight() ? image.getHeight() : endY;
		count = (endX - startX) * (endY - startY);

		for (int x = startX; x < endX; x++) {
			for (int y = startY; y < endY; y++) {
				int rgb = image.getPixel(x, y);

				int r = Color.red(rgb);
				int g = Color.green(rgb);
				int b = Color.blue(rgb);

				totalRed += r;
				totalGreen += g;
				totalBlue += b;

				totalRedSqr += (r * r);
				totalGreenSqr += (g * g);
				totalBlueSqr += (b * b);
			}
		}

		meanRed = totalRed / count;
		meanBlue = totalBlue / count;
		meanGreen = totalGreen / count;

		varianceRed = (totalRedSqr - totalRed * meanRed) / (count - 1);
		varianceBlue = (totalBlueSqr - totalBlue * meanBlue) / (count - 1);
		varianceGreen = (totalGreenSqr - totalGreen * meanGreen) / (count - 1);
		varianceTotal = varianceRed + varianceBlue + varianceGreen;

		AreaInfo ret = new AreaInfo(varianceTotal, meanRed, meanBlue, meanGreen);
		return ret;
	}

	private class AreaInfo {
		public double mMeanRed = 0;
		public double mMeanBlue = 0;
		public double mMeanGreen = 0;
		public double mTotalVariance = 0;

		AreaInfo(double totalV, double mRed, double mBlue, double mGreen) {
			mTotalVariance = totalV;
			mMeanRed = mRed;
			mMeanBlue = mBlue;
			mMeanGreen = mGreen;
		}
	}

	private class AreaInfoComparator implements Comparator<AreaInfo> {
		public int compare(AreaInfo a1, AreaInfo a2) {
			if (a1.mTotalVariance > a2.mTotalVariance) {
				return 1;
			} else {
				return -1;
			}
		}
	}
}